package org.biogroovy.search

import groovy.transform.AutoClone

import java.text.DateFormat
import java.text.NumberFormat
import java.text.SimpleDateFormat
import groovy.transform.TupleConstructor

/**
 * This class represents a search parameter.
 * @param <T> parameter type
 */

@TupleConstructor
@AutoClone
class SearchParam<T> implements Serializable{

	/** The name of the parameter. (MUST BE unique). */
	String name;

	/** The name used when creating a request parameter. (MAY NOT BE unique). If the data name is no set, then the name will be used*/
	String dataName;

	/** The value of the parameter. */
	String value;

	/** Indicates that the parameter is required. */
	boolean isRequired;

	/** Indicates that the parameter is to be added to the main 'term' parameter. */
	boolean isSubTerm = false;

	/** Indicates that the search parameter is editable. If false, then the parameter is assumed to be static.*/
	boolean isEditable = true;

	/** Is the search term parameter. */
	boolean isTerm = false;
	
	/** A hint to be displayed concerning the search parameter. */
	String hint;

	/**
	 * A map of allowable values.  The keys are displayable values (as they might appear in a UI), and the values
	 * are the values that are actually stored.
	 */
	Map<String, String> allowableValueMap = new HashMap<>();

	/**
	 * A list of validators for the parameter.
	 */
	List<ISearchParamValidator> validatorList = new ArrayList<>();

	/** A list of validation messages. */
	List<String> validationMsgs = new ArrayList<>();

	/** The parameter type. */
	SearchParamType paramType = SearchParamType.STRING;

	/** The format of the date. */
	DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

	/** The format of the number. */
	NumberFormat numberFormat = NumberFormat.getInstance();
	
	


	/** 
	 * This method determines if the parameter has a value.
	 * @return true if the value has been set; false, otherwise.
	 */
	public boolean hasValue(){
		return value != null;
	}

	public String getValue(){
		return value;
	}

	public void setValue(T value){
		if (value instanceof String){
			setStringValue(value);
		}else if (value instanceof Date){
			setDateValue(value);
		}else if (value instanceof Number){
			setNumericValue(value);
		}
	}

	public void setDateValue(Date date){
		value = dateFormat.format(date);
	}

	public void setNumericValue(Number number){
		value = numberFormat.format(number);
	}

	public void setStringValue(String val){
		value = val;
	}


	/**
	 * This method determines if there is a list of allowable
	 * values associated with this parameter.
	 * @return true, if there is a list of allowable values.
	 */
	public boolean hasAllowableValues(){
		return !allowableValueMap.isEmpty();
	}

	/**
	 * This method gets the parameter name used when building a URL.
	 * It checks to see if the 'dataName' attribute is set, and uses this
	 * value by default.  If the 'dataName' is not set, then it uses the 'name'.
	 * @return the parameter name
	 */
	public String getParamName(){
		return (dataName != null)?dataName:name;
	}

	/**
	 * This method adds validators to the parameter.
	 * @param validators the validators to be added.
	 */
	public void addValidators(ISearchParamValidator... validators){
		validatorList.addAll(validators);
	}

	/**
	 * This method validates the contents of the parameter.  If the parameter
	 * is invalid, then it saves any validation messages in a list.
	 * @return
	 */
	public boolean isValid(){
		boolean isValid = true;

		for(ISearchParamValidator validator : validatorList){
			if(!validator.apply(this)){
				validationMsgs.addAll(validator.getValidationMessages());
				isValid = false;
			}
		}

		return isValid;
	}

	public List<String> getValidationMessages(){
		return validationMsgs;
	}

	/**
	 * This method sets the allowable values and assumes that the values
	 * to be displayed are the same as the values that are stored.
	 * @param values the set of values.
	 */
	public void setAllowableValues(String... values){
		for(String value : values){
			allowableValueMap.put(value, value);
		}
	}

	/**
	 * This method sets the allowable values using a map of key-value pairs.
	 * The keys are the displayable values, the values are the values as they are stored.
	 * @param valueMap the value map.
	 */
	public void setAllowableValueMap(Map<String, String> valueMap){
		allowableValueMap = valueMap
	}
	
	@Override
	public String toString(){
		return "SearchParam[name:${name}, value:${value}, dataName:${dataName}, isEditable:${isEditable}, isRequired:${isRequired}, isTerm:${isTerm}, isSubTerm:${isSubTerm}, paramType:${paramType}]"
	}
	
}
